class Vue {
    constructor(options) {
       this.$options = options
       this._data = options.data
       this.initData()
    }
    initData() {
       let data = this._data
       let keys = Object.keys(data)
       for(let i=0;i<keys.length;i++) {
          Object.defineProperty(this, keys[i], {
             enumerable: true,
             configurable: true,
             get() {
                return data[keys[i]]
             },
             set(value) {
                data[keys[i]] = value
             }
          })
       }
       observe(data)
       this.initWatch()
    }
    initWatch() {
       let watch = this.$options.watch
       if(watch) {
          let keys = Object.keys(watch)
          for(let i=0;i<keys.length; i++) {
             new Watcher(this, keys[i], watch[keys[i]])
          }
       }
    }
    $watch(exp, cb) {
       new Watcher(this, exp, cb)
    }
    $set(target, key, value) {
        defineReactive(target, key, value)
        target.__ob__.dep.notify()
    }
 }
 
 function observe(data) {
    let type = Object.prototype.toString.call(data)
    if(type !== '[object Object]' && type!=='[object Array]') return 
    if(data.__ob__){
        return data.__ob__
    }
    return new Observer(data) 
 }
 
 function defineReactive(obj, key, value) {
    let childob = observe(obj[key])
    let dep = new Dep()
    Object.defineProperty(obj, key, {
       enumerable: true,
       configurable: true,
       get() {
          dep.depend()
          if(childob){
            childob.dep.depend()
          }
        //   if(obj.__ob__) {
        //     obj.__ob__.dep.depend()
        //   }
          return value
       },
       set(val) {
          if(val === value) return
          value = val
          dep.notify()
       }
    })
 }
 
 class Observer {
    constructor(data) {
       this.dep = new Dep()
       this.walk(data)
       Object.defineProperty(data, '__ob__', {
            value: this,
            enumerable: false,
            configurable: true,
            writable: true
        })
    }
    walk(data) {
       let keys = Object.keys(data)
       for(let i=0;i<keys.length;i++) {
          defineReactive(data, keys[i], data[keys[i]])
       }
    }
 }
 
 class Dep {
    constructor() {
       this.subs = []
    }
    depend() {
       if(Dep.target) {
          this.subs.push(Dep.target)
       }
    }
    notify() {
       this.subs.forEach((watcher) => {
          watcher.run()
       })
    }
 }
 
 let watcherId = 0
 let watcherQueue = []
 
 class Watcher {
    constructor(vm, exp, cb) {
       this.vm = vm
       this.exp = exp
       this.cb = cb
       this.id = ++watcherId
       this.get()
    }
 
    get() {
       Dep.target = this
       this.vm[this.exp]
       Dep.target = undefined
    }
 
    run() {
       if(watcherQueue.indexOf(watcherId) !== -1) return
       watcherQueue.push(this.id)
       let index = watcherQueue.length -1
       // 立刻返回一个 promise 实例
       Promise.resolve().then(() => {
          this.cb.call(this.vm)
          watcherQueue.splice(index, 1) // 这一句应当放在 then 函数当中，否则 index 会被缓存(会不会和闭包有关系)
       })      
    }
 }